package timing.ukulele.gateway.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;
import timing.ukulele.common.data.ResponseData;
import timing.ukulele.gateway.config.properties.SecurityProperties;
import timing.ukulele.gateway.http.api.PortalApi;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SecurityUserDetailService {
    private final PortalApi portalApi;
    private final SecurityProperties securityProperties;

    public SecurityUserDetailService(
            PortalApi portalApi,
            SecurityProperties securityProperties) {
        this.portalApi = portalApi;
        this.securityProperties = securityProperties;
    }

    public boolean checkUserRoleResource(String path, String method, String username, String orgId) {
        if (!StringUtils.hasText(username) || !StringUtils.hasText(path) || !StringUtils.hasText(method)) {
            return false;
        }
        PathMatcher pathMatcher = new AntPathMatcher();
        List<String> permitUrlList = securityProperties.getPermitUrlList();
        if (!CollectionUtils.isEmpty(permitUrlList)) {
            long count = permitUrlList.stream().filter(item -> pathMatcher.match(item, path)).count();
            if (count > 0)
                return true;
        }
        long org;
        if (StringUtils.hasText(orgId)) {
            org = Long.parseLong(orgId);
        } else {
            org = 0L;
        }
        Mono<ResponseData<Map<String, Set<String>>>> responseMono = portalApi.findByUsername(username, org);
        // WebFlux异步调用，同步会报错 todo 应该不是一个好办法
        CompletableFuture<ResponseData<Map<String, Set<String>>>> task = CompletableFuture.supplyAsync(responseMono::block);
        try {
            ResponseData<Map<String, Set<String>>> response = task.get();
            if (response == null || CollectionUtils.isEmpty(response.getData())) {
                return false;
            }

            Map<String, Set<String>> resourceMap = response.getData();

            method = method.toUpperCase();
            for (Map.Entry<String, Set<String>> entry : resourceMap.entrySet()) {
                boolean match = pathMatcher.match(entry.getKey(), path);
                if (match) {
                    Set<String> methods = entry.getValue();
                    if (!CollectionUtils.isEmpty(methods)) {
                        if (methods.contains("*")) {
                            return true;
                        } else {
                            if (methods.contains(method))
                                return true;
                        }
                    }
                }
            }
        } catch (Exception exception) {
            exception.printStackTrace();
            log.error(exception.getMessage());
        }
        return false;
    }

}
